package com.asa.hutils.teizheng;


import com.asa.hutils.teizheng.structure.Matrix;




public abstract class AbstractHouseholderReflection {

	public Matrix getHouseholderReflectionAtACol(Matrix R, int column) {
		/* Matrix H = I - 2*P  */


		
		Matrix I = getIdentityMatrix(R.rows());	
//		I.print();
		
		Matrix P = getMatrixPAtSpecifiedColumn(R, column);
		if(P == null) {
			return null;
		}		
		Matrix doubleP = P.multiply(2);
		
		
		return I.subtract(doubleP);
	}
	
	private Matrix getMatrixPAtSpecifiedColumn(Matrix R, int column) {
		/* Matrix p = v*v' / v'*v */
		Matrix v = getMatrixVAtSpecifiedColumn(R, column);		
		Matrix transposeOfV = v.t();
		Matrix vMultiVt = v.multiply(transposeOfV);					
		Matrix vtMultiV = transposeOfV.multiply(v); 	


		
		if(vtMultiV.at(0,0) == 0) {
			return null;
		}
		
		Matrix P = vMultiVt.multiply(1/vtMultiV.at(0, 0));						
		return P;
	}

	protected abstract double[] getXAtSpecifiedColumn(Matrix R, int column);

	private double[] getVAtSpecifiedColumn(double[] x, double[] w) {
		double[] v = new double[w.length];
		int sign = 1;
		if(x[0] > 0) {
			sign *= -1;
		}		
		for(int m=0; m<v.length; m++) {
			v[m] = w[m] + sign * x[m];
		}
		return v;
	}

	protected abstract double[] getWAtSpecifiedColumn(int column, double[] x);

	private Matrix getMatrixVAtSpecifiedColumn(Matrix R, int column) {
		/* Matrix v = w - x   */
		double[] x = getXAtSpecifiedColumn(R, column);
		double[] w = getWAtSpecifiedColumn(column, x);
		double[] v = getVAtSpecifiedColumn(x, w);			
		
		//Convert v to a two dimension matrix
		Matrix packageOfV = new Matrix(v.length, 1);
		for(int m=0; m<packageOfV.rows(); m++) {
			packageOfV.set(m, 0, v[m]);
		}
		return packageOfV;
	}
	
	private Matrix getIdentityMatrix(int size) {
		Matrix I = new Matrix(size, size);
		for(int i=0; i<I.rows(); i++) {
			for(int j=0; j<I.columns(); j++) {
				if(i == j) {
					I.set(i, j, 1);
				} else {
					I.set(i, j, 0);
				}
			}
		}
		return I;
	}
}
